package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.ISkuStockService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.product.vo.SkuLockVo;
import com.spzx.product.vo.SkuStockVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 商品sku库存表 服务实现类
 * </p>
 *
 * @author atguigu
 * @since 2025-02-19
 */
@Service
public class SkuStockServiceImpl extends ServiceImpl<SkuStockMapper, SkuStock> implements ISkuStockService {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询库存
     *
     * @param skuId
     * @return
     */
    @Override
    public SkuStockVo getSkuStock(Long skuId) {
        LambdaQueryWrapper<SkuStock> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SkuStock::getSkuId, skuId);
        SkuStock skuStock = baseMapper.selectOne(queryWrapper);
        if (skuStock == null) {
            throw new ServiceException("商品库存不存在！");
        }
        SkuStockVo skuStockVo = new SkuStockVo();
        skuStockVo.setSkuId(skuId);
        skuStockVo.setAvailableNum(skuStock.getAvailableNum());
        skuStockVo.setSaleNum(skuStock.getSaleNum());
        return skuStockVo;
    }

    /**
     * 检查与锁定库存
     *
     * @param orderNo
     * @param skuLockVoList
     * @return 库存失败信息：包含库存不足商品以及现有库存数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        //1.检查库存,采用悲观锁：在一个事务内对库存记录锁定，锁定其他其他事务不可以修改库存记录
        //1.1 遍历扣减商品列表，对扣减每件商品库存记录 进行悲观锁 上锁
        skuLockVoList.forEach(skuLockVo -> {
            //检查库存
            SkuStock skuStock = baseMapper.checkStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (skuStock != null) {
                //扣库充足
                skuLockVo.setHaveStock(true);
            } else {
                //库存不足
                skuLockVo.setHaveStock(false);
            }
        });

        //2.判断扣减商品列表是否全部有库存，如果有才扣减。如果有任意商品库存不足封装错误信息返回
        StringBuilder lockFailMsg = new StringBuilder();
        boolean flag = skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.isHaveStock());
        if (flag) {
            //存在库存不足商品
            skuLockVoList.forEach(skuLockVo -> {
                if (!skuLockVo.isHaveStock()) {
                    //根据skuId查询可用库存
                    Integer availableNum = baseMapper.selectOne(new LambdaQueryWrapper<SkuStock>().eq(SkuStock::getSkuId, skuLockVo.getSkuId())).getAvailableNum();
                    lockFailMsg.append(skuLockVo.getSkuName())
                            .append("库存不足，现有库存数：")
                            .append(availableNum)
                            .append(";");
                }
            });
            return lockFailMsg.toString();
        }

        //3.如果检查成功，更新库存记录，更新：锁定数加，可用数减
        for (SkuLockVo skuLockVo : skuLockVoList) {
            baseMapper.lockStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
        }
        //4.为了后续订单取消后解锁或订单支付成功后扣减，将锁定信息存入redis中 有效期：2小时  订单支付有效期：1小时
        String lockStockKey = "sku:lock:data:" + orderNo;
        redisTemplate.opsForValue().set(lockStockKey, skuLockVoList, 2, TimeUnit.HOURS);
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unlockStock(String orderNo) {
        String lockStockKey = "sku:lock:data:" + orderNo;
        List<SkuLockVo>  skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(lockStockKey);
        if(!CollectionUtils.isEmpty(skuLockVoList)){
            skuLockVoList.forEach(skuLockVo -> {
                baseMapper.unlockStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            });
        }
        //恢复锁定库存后 删除Redis中的锁定库存信息
        redisTemplate.delete(lockStockKey);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void minusStock(String orderNo) {
        //1.当时 保存订单锁定库存信息被存入Redis 中，所以需要从Redis中获取
        String lockStockKey = "sku:lock:data:" + orderNo;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(lockStockKey);
        if(!CollectionUtils.isEmpty(skuLockVoList)){
            skuLockVoList.forEach(skuLockVo -> {
                baseMapper.minusStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            });
        }
    }
}
